<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        body,
        ul,
        li {
            padding: 0;
            margin: 0;
        }

        ul {
            list-style-type: none
        }

        .rating {
            width: 300px;
            height: 47px;
        }

        .rating-item {
            float: left;
            width: 48px;
            height: 47px;
            background: url(img/rating.png) no-repeat 0 0px;
            /* 将多张图片合并到一起，可以降低网络开销 */
            cursor: pointer
        }
    </style>
</head>

<body>

    <ul class="rating" id="rating">

        <li class="rating-item" title="很不好"></li>
        <li class="rating-item" title="不好"></li>
        <li class="rating-item" title="一般"></li>
        <li class="rating-item" title="好"></li>
        <li class="rating-item" title="很好"></li>

    </ul>
    <script src="http://libs.baidu.com/jquery/1.11.3/jquery.min.js"></script>
    <script>
        var rating = (function () {

            // 继承原型上的方法：
            /* lightEntire.prototype=new light();
            lightHalf.prototype=new light(); */
            // 3、借用一个空类去点多余的字段来实现方法的继承
            var extend = function (subClass, superClass) {
                var F = function () {};
                F.prototype = superClass.prototype;
                subClass.prototype = new F();
                subClass.prototype.constructor = subClass; //特别注意constructor的指向
            };

            // 父类
            // 1、抽取父类（将简单的对象想复杂的对象靠拢，lighton和bindEvent是两种不同的抽取方式，合理评估使用哪种）
            var light = function (el, options) {
                this.$el = $(el);
                this.$item = this.$el.find(".rating-item");
                this.opts = options;
                this.addStep = 1;
            }
            light.prototype.init = function () {
                this.lightOn(this.opts.num);
                this.bindEvent();
            };
            light.prototype.lightOn = function (num) {
                num = parseInt(num);
                this.$item.each(function (index) {
                    if (index < num) {
                        $(this).css('background-position', '0 -94px');
                    } else {
                        $(this).css("background-position", '0 0');
                    }
                })
            };
            light.prototype.bindEvent = function () {

                var itemLength = this.$item.length;
                var that = this;

                this.$el.on(that.selectEvent, ".rating-item", function (event) {
                    var $this = $(this),
                        num = 0;
                    that.select(event, $this);
                    num = $(this).index() + that.addStep;
                    that.lightOn(num);
                    (typeof that.opts.select === 'function') && that.opts.select.call(this, num,
                        itemLength);

                }).on("click", ".rating-item", function () {
                    that.lightOn($(this).index() + that.addStep);
                    that.opts.num = $(this).index() + that.addStep;
                    (typeof that.opts.chosen === "function") && that.opts.chosen.call(this, that.opts
                        .num,
                        itemLength);
                }).on("mouseout", ".rating-item", function () {
                    that.lightOn(that.opts.num);
                })
            }
            light.prototype.select = function () {
                throw new Error("子类必须重写");
            }

            // 点亮整颗
            var lightEntire = function (el, options) {
                // 2、使用call的方式来继承字段
                light.call(this, el, options);
                this.selectEvent = "mouseover";
            }
            extend(lightEntire, light);
            lightEntire.prototype.lightOn = function (num) {
                light.prototype.lightOn.call(this, num);
            };
            lightEntire.prototype.select = function () {};

            // 点亮半颗
            var lightHalf = function (el, options) {
                light.call(this, el, options);
                this.selectEvent = "mousemove";
            }
            extend(lightHalf, light);
            lightHalf.prototype.lightOn = function (num) {
                var count = parseInt(num),
                    isHalf = count !== num;

                light.prototype.lightOn.call(this, count);

                if (isHalf) {
                    this.$item.eq(count).css("background-position", "0 -47px");
                }
            };
            lightHalf.prototype.select = function (event, $this) {
                if (event.pageX - $this.offset().left < $this.width() / 2) {
                    this.addStep = 0.5;
                } else {
                    this.addStep = 1;
                }
            };


            // 如果不是用extend方法，而是直接new一个对象来继承方法，light中的字段会有两份，占用内存。
            /* var lightHalf = function (el, options) {
                light.call(this, el, options);
                this.selectEvent = "mousemove";
            }
            lightHalf.prototype = new light();
            lightHalf.prototype.lightOn = function (num) {
                var count = parseInt(num),
                    isHalf = count !== num;

                light.prototype.lightOn.call(this, count);

                if (isHalf) {
                    this.$item.eq(count).css("background-position", "0 -47px");
                }
            };
            lightHalf.prototype.select = function (event, $this) {
                if (event.pageX - $this.offset().left < $this.width() / 2) {
                    this.addStep = 0.5;
                } else {
                    this.addStep = 1;
                }
            }; */

            // 默认参数：建立了一套模板
            var defaults = {
                num: 0,
                readOnly: false,
                select: function () {},
                chosen: function () {},
                mode: "lightEntire"
            }

            // 不同的mode用不同的对象进行封装，用一个mode来控制使用不同的对象
            var modeMap = {
                "lightHalf": lightHalf,
                "lightEntire": lightEntire
            };

            // 初始化
            var init = function (el, options) {
                var option = $.extend({}, defaults, options);
                if (!modeMap[options.mode]) {
                    options.mode = "lightHalf";
                }
                var elem = new modeMap[options.mode](el, option);
                elem.init();
            };

            return {
                init: init
            }
        })();

        rating.init("#rating", {
            num: 2.5,
            mode: "lightHalf",
            select: function (index, length) {
                console.log('当前：' + index + '，一共：' + length);
            }
        })
    </script>

</body>

</html>